(*  :Title:	z-Transform Rule Base  *)

(*  :Authors:	Brian Evans, James McClellan  *)

(*
    :Summary:	To take the forward, multi-dimensional z-transform
		of a discrete-time expression of mathematical
		and signal processing structures.
 *)

(*  :Context:	SignalProcessing`Digital`ZTransform`  *)

(*  :PackageVersion:  2.7	*)

(*
    :Copyright:	Copyright 1989-1991 by Brian L. Evans
		Georgia Tech Research Corporation

	Permission to use, copy, modify, and distribute this software
	and its documentation for any purpose and without fee is
	hereby granted, provided that the above copyright notice
	appear in all copies and that both that copyright notice and
	this permission notice appear in supporting documentation,
	and that the name of the Georgia Tech Research Corporation,
	Georgia Tech, or Georgia Institute of Technology not be used
	in advertising or publicity pertaining to distribution of the
	software without specific, written prior permission.  Georgia
	Tech makes no representations about the suitability of this
	software for any purpose.  It is provided "as is" without
	express or implied warranty.
 *)

(*  :History:	*)

(*  :Keywords:	z-transform, region of convergence	*)

(*
    :Source:	    Muth, E. J. {Transform Methods}.  1977

		    Oppenheim and Schafer.  {Digital Signal Processing}. 1973.

	  	    Clements, M., and Pease, J.  "On Causal Linear Phase IIR
		Digital Filters". Transactions of Acoustics, Speech, and
		Signal Processing (ASSP).  April 1989

		    Dungeon and Mersereau.  {Multidimensional Signal
		Processing}. 1984.
 *)

(*
    :Warning:	This package stuff is nasty; reminds me of Lisp, except
		  that Lisp had package-independent keywords like  :hi.
		  Remember,  that only those symbols with a ::usage are
		  exported.
		Keep all conditional clauses on the same line; breaking
		  long clauses  onto  separate  lines with RETURNS will
		  cost you (package context is not reset to Global)
		I do rely  on  three  variables that are global only to
		  this package--  completeOptions, dialogueAllFlag, and
		  generatePairsFlag.  The first maintains a list of the
		  complete set of options.  The  second  specifies  the
		  level of dialogue. The third indicates whether or not
		  to simplify the resulting transform. I'm really sorry
		  for doing this--- I know that it is bad programming.
 *)

(*  :Mathematica Version:  1.2 or 2.0  *)

(*  :Limitation:  *)

(*  :Discussion:  All new functions have usage information.
		  Local state rules base with a total of 76 rules:
		    I.   multidimensional hooks		 9 rules
		    II.  rational transforms		15 rules
		    III. non-rational transform pairs	 7 rules
		    IV.  transform properties		13 rules
		    V.   transforms of DSP structures	22 rules
		    VI.  transform strategies		10 rules

	The only purpose to the multidimensional hook rules is to track
	the region of convergence for non-separable functions.  Without
	these rules, the  z-transform  rule  base  would still properly
	compute the z-transform function.   Otherwise, the DTFTransform
	would not work properly for functions  like  (6/7)^n1 (9/10)^n2
	Impulse[n1 - n2] Step[n1, n2]  with respect to the variables n1
	and n2.

	At each step in the z-transform rules base, the current expression
	has a local state associated with it.  This state consists of
	a list of five boolean values.  Each boolean value is associated
	with a strategy.  If an element is True, then that strategy has not
	been tried yet; if False, then that strategy has already been tried,
	and it will not be tried again.  Thus, local state is used to
	prevent infinite loops which would be caused by the repetitive
	application of certain strategy rules.  See the section  S T A T E
	D E F I N I T I O N  below and see section VI of the rules.

	MyZTransform[ f[n], n, z, state, nlist, zlist, options ] is similar
	to ZTransform.  The one-dimensional z-transform of f[n] will be
	returned as a list of three pieces of data:  X(z), Rminus, Rplus.
	The current dimension being transformed is indicated by atoms n
	and z; nlist contains the list of all time index variables
	used in f[n]; and zlist contains the z-transform variables.
	Note that ZTransform drives the rule base defined by MyZTransform
	when computing the forward z-transform.

	multidROC[zexpr, oldzexpr] accumulates region of convergence
	information.  That is, ROC of the current z-transform is only
	one-dimensional; therefore, zexpr must be updated with the
	cumulative ROC information in oldzexpr.
 *)


(*  :Functions:  ZTransform  *)



(*  B E G I N     P A C K A G E  *)

BeginPackage[ "SignalProcessing`Digital`ZTransform`",
	      "SignalProcessing`Digital`ZSupport`",
	      "SignalProcessing`Support`TransSupport`",
	      "SignalProcessing`Support`ROC`",
	      "SignalProcessing`Support`FilterSupport`",
	      "SignalProcessing`Support`SigProc`",
	      "SignalProcessing`Support`SupCode`" ]


If [ TrueQ[ $VersionNumber >= 2.0 ],
     Off[ General::spell ];
     Off[ General::spell1 ] ];


(*  U S A G E     I N F O R M A T I O N  *)

ZTransform::usage =
	"ZTransform[e,n] or ZTransform[e,n,z] gives the z-transform of \
	the expression e, which is a function of n, by returning an object \
	of three slots which is tagged by ZTransData: \
	<z-transform>, <rminus>, <rplus>, and <z-variables>. \
	The Region of Convergence (ROC) is <rminus> < |z| < <rplus>. \
	Note that the returned ROC is either the actual ROC or a subset \
	of the actual ROC. \
	In two dimensions, ZTransform[e, {n1, n2}, {z1, z2}], is the same as \
	ZTransform [ ZTransform [e, n1, z1], n2, z2 ]. \
	This notation extends naturally to higher dimensions."

(*  E N D     U S A G E     I N F O R M A T I O N  *)


Begin[ "`Private`" ]


(*  M E S S A G E S  *)

ZTransform::notexist =
	"The function `` does not have a valid z-transform with respect to ``."

ZTransform::badROC = "Improper region of convergence in ``." 


(*  U S E R     I N T E R F A C E  *)

(*  Z Operator  *)
Unprotect[Z]
Z/: TheFunction[ Z[n_, z_][f_] ] := ZTransform[f, n, z]
Protect[Z]

(*  ZTransform  *)
ZTransform/: Options [ ZTransform ] :=
	{ Definition -> False, Dialogue -> True,
	  Simplify -> True, TransformLookup -> {} }

ZTransform[f_] :=
	ztransformdriver[ Options[ZTransform], f ]
ZTransform[f_, n_] :=
	ztransformdriver[ Options[ZTransform], f, n ]
ZTransform[f_, n_, z_] :=
	ztransformdriver[ Options[ZTransform], f, n, z ]
ZTransform[f_, n_, z_, op__] :=
	ztransformdriver[ ToList[op] ~Join~ Options[ZTransform], f, n, z ]

(*  data global to only this package, see :Warning: above *)
completeOptions = {}
dialogueAllFlag = False
indentationString = ""
simplifyFlag = False

(*  validvarQ  *)
validvarQ[ x_Symbol ] := True
validvarQ[ x_List ] := Apply[And, Map[VariableQ, x]]
validvarQ[ x_ ] := False

(*  Interface support  *)
ztransformdriver[op_, f_, n_, rest___] :=
	Message[ Transform::badvar, "discrete-time", ZTransform, n ] /;
	! validvarQ[n]

ztransformdriver[op_, f_, n_, z_] :=
	Message[ Transform::badvar, "z", ZTransform, z ] /;
	! validvarQ[z]

ztransformdriver[op_, f_, n_, z_] :=
	Block [ {},

		(* Set all global variables right here *)
		completeOptions = op;
		dialogueAllFlag = SameQ[Replace[Dialogue, op], All];
		simplifyFlag = TrueQ[Replace[Simplify, op]];

		(* Do the transform  *)
		cleanup [ ztransform[op, f, n, z], InformUserQ[op] ] ]

ztransform[op_, args__] :=
	Replace [ ztrans[op, args], ZTransformInterfaceRules ]
 
ZTransformInterfaceRules = {
	ztrans[op_, e_] :> e		/; ZTransformQ[e],

	ztrans[op_, e_] :>
		Message[Transform::novariables, "n (time)", GetVariables[e]],

	ztrans[op_, e_, n_] :> e	/; ZTransformQ[e],

	ztrans[op_, e_, n_] :>
		ztransform[op, e, n, DummyVariables[Length[n], Global`z]],

	ztrans[op_, e_, n_, zl_] :>
		MultiDTransform[ MakeZObject, ztransform,
				 ZTransformQ, e, n, z, zl, op ] /;
		ListQ[n] && ( AtomQ[zl] || ListQ[zl] ),

	ztrans[op_, e_, n_, z_] :> ztransform[op, e, n, z, {n}, {z}] /;
		AtomQ[n] && AtomQ[z],

	ztrans[op_, e_, n_, z_, nlist_, zlist_] :>
		If [ SameQ[ToList[ZVariables[e]], zlist],
		     e,
		     multidROC[ztransform[op, TheFunction[e], n, z, nlist, zlist],
			       e] ] /;
		ZTransformQ[e] && AtomQ[n] && AtomQ[z],

	ztrans[op_, e_, n_, z_, nlist_, zlist_] :>
		MakeZObject[ MyZTransform [ e, n, z, nlist, zlist, op ], z ] /;
		! ZTransformQ[e] && AtomQ[n] && AtomQ[z]
}

(*  cleanup passes every subexpression of the transform through explain  *)
cleanup[Null, flag_] := flag

cleanup[trans_, dialogue_] :=
	Block [	{dialflag, rminus, rplus, trace, valid, validROC = True},
		dialflag = InformUserQ[dialogue];
		trace = SameQ[dialogue, All];
		valid = SameQ[Head[trans], ZTransData];
		If [ valid,
		     rminus = SPSimplify[ GetRMinus[trans] ];
		     rplus = SPSimplify[ GetRPlus[trans] ];
		     validROC = Apply[And,
				      Thread[ToList[rminus] < ToList[rplus]]] ];
		Which [ TrueQ[! validROC],
		          Message[ZTransform::badROC, trans],
			valid && simplifyFlag,
			  SPSimplify[ trans, Dialogue -> trace ],
			dialflag && ! valid,
			  Scan[explain, trans, Infinity];
			  trans,
			True,
			  trans ] ]

(*  explain tells the user about those parts of the  *)
(*  "time"-function which do not have a transform.   *)
explain[ forwardz[f_, n_, rest__] ] :=
	Message[ Transform::incomplete, "forward z-transform", f, n ]


(*  S U P P O R T I N G     R O U T I N E S  *)

absDialogue[f_, n_, options_] :=
	Block [	{dialogue, result},
		dialogue = InformUserQ[options];
		result = ( f /. {Abs[a_. n] :> Abs[a] n} ) Step[n] +
			 ( f /. {Abs[b_. n] :> - Abs[b] n} ) Step[-n - 1];

		If [ dialogue,
		     Print[ "( after rewriting the two-sided expression" ];
		     Print[ "  ", f ];
		     Print[ "  as a left-sided plus a right sided sequence" ];
		     Print[ "  ", result, " )" ] ];

		result ]

addZ[t1_, t2_] := AddT[ZForm, t1, t2]

allROC[t_] := Transform[TheFunction[t], 0, Infinity] /; ZForm[t]

antiCausalZ[t_, z_] :=
	Transform[ TheFunction[t] /. z -> z^-1,
		   1 / GetRPlus[t], 1 / GetRMinus[t] ] /;
	ZForm[t]

applyDefinition[x_, n_, z_, s_, nlist_, zlist_, op_] :=
        Block [ {newx, state, summhead, trans},
		state = SetStateField[s, definitionfield, False];
                summhead = If [ TrueQ[ $VersionNumber >= 2.0 ],
				Needs[ "Algebra`SymbolicSum`" ];
				Algebra`SymbolicSum,
				Needs[ "Algebra`GosperSum`" ];
				Algebra`GosperSum ];
		newx = ToDiscrete [ TheFunction[x] ];
		trans = summhead[newx z^(-n), {n, -Infinity, Infinity}];
		If [ SameQ[Head[trans], summhead],
		     forwardz[x, n, z, state, nlist, zlist, op],
		     TransformDialogue[ Definition, op, summhead, x,
					Transform[trans, 0, Infinity] ] ] ]

computeDownsamplingFactor[f_, n_] := Numerator[ ScalingFactor[f, n] ]

conjZ[t_, z_] := ConjT[ZForm, t, z]

convolveZ[convop_, t1_, t2_] := ConvolveT[ZForm, convop, t1, t2]

(*  diagonalResamplingMatrixQ  *)
diagonalResamplingMatrixQ[mat_] := False  /; ! MatrixQ[mat]
diagonalResamplingMatrixQ[mat_] :=
	Block [ {cond = False, diag, dims, i},
		dims = Dimensions[mat];
		If [ dims[[1]] == dims[[2]],
		     diag = Table[ mat[[i,i]], { i, 1, dims[[1]] } ];
		     cond = Apply[And, Map[isinteger, diag] ] &&
			    SameQ[mat, DiagonalMatrix[diag]] ];
		cond ]


downsampledTransform[trans_, z_, m_] :=
	Block [	{fztrans, k},
		fztrans = trans /. z -> ( Exp[-2 Pi I k / m] z^(1/m) );
		If [ IntegerQ[m],
		     1/Abs[m] Sum[fztrans, {k, 0, Abs[m]-1}],
		     k = Unique["k"];
		     1/Abs[m] Summation[k, 0, Abs[m]-1, 1][fztrans] ] ]

downsampleZ[ztrans_, z_Symbol, m_] :=
	Block [	{fz, fztrans, k, rm, rp},
		rm = takeRealPower[ GetRMinus[ztrans], m ];
		rp = takeRealPower[ GetRPlus[ztrans], m ];
		fz = downsampledTransform[ TheFunction[ztrans], z, m ];
		Transform[fz, rm, rp] ] /;
	ZForm[ztrans]

dz[t_, z_:Global`z, k_:1] := 
	Transform[ D[TheFunction[t], {z, k}], GetRMinus[t], GetRPlus[t] ] /;
	ZForm[t]

(*  fixUp: removes TransformLookup option from passed options  *)
fixUp[ op_ ] :=
	{ Definition -> Replace[Definition, op],
	  Dialogue -> Replace[Dialogue, op],
	  Simplify -> Replace[Simplify, op] }

integrateZ[t_, z_:Global`z] := IntegrateT[ZForm, t, z, z, Infinity]

isinteger[x_] := Implies[ NumberQ[x], IntegerQ[x] ]

isintegerMatrix[ x_ ] :=
	MatrixQ[x] && Apply[ SameQ, Dimensions[x] ] && 
	Apply[ And, Map[isinteger, Flatten[x]] ]

lineImpulseMDZ[t_, z_, zlist_, nleft_] :=
	LineImpulsemDT[ZForm, t, z, zlist, nleft, Times]

(*  mDresamplingCheck  *)
mDresamplingCheck[separable, l_, n_, nvars_, nlist_] :=
	MyFreeQ[l, nvars] && SubsetQ[{n}, nvars, nlist] &&
	  diagonalResamplingMatrixQ[l]

mDresamplingCheck[Downsample, l_, n_, nvars_, nlist_] :=
	Block [	{cond},
		cond = MyFreeQ[l, nvars] && SubsetQ[{n}, nvars, nlist] &&
			MatrixQ[l] && Apply[SameQ, Dimensions[l]] &&
			Apply[ And, Map[IntegerQ, Flatten[l]] ];
		If [ cond, Needs[ "SignalProcessing`Digital`MDZTransform`" ] ];
		cond ]

mDresamplingCheck[Upsample, l_, n_, nvars_, nlist_] :=
	Block [ {cond},
		cond = MyFreeQ[l, nvars] && SubsetQ[{n}, nvars, nlist] &&
			isintegerMatrix[l];
		If [ cond, Needs[ "SignalProcessing`Digital`MDZTransform`" ] ];
		cond ]

multZ[t1_, t2_] := MultT[ZForm, t1, t2]

multiplyByExponential[t_, aexp_, z_, c_, options_] :=
	Block [	{newrplus, rplus},
		rplus = GetRPlus[t];
		newrplus = If [ InfinityQ[rplus], Infinity, Abs[aexp] rplus ];
		Transform[ c TheFunction[t] /. z -> z / aexp,
			   Abs[aexp] GetRMinus[t], newrplus ] ] /;
	ZForm[t]

myZmultinomialLeftOver[f_, nvars__] :=
	myZmultinomialSupport[Apply[Times, Map[Factorial, {nvars}]^-1], f] [[2]]

myZmultinomialQ[f_, nvars__] :=
	TrueQ [ myZmultinomialSupport[Apply[Times, Map[Factorial, {nvars}]^-1], f] [[1]] ]

myZmultinomialSupport[factexpr_, f_. factexpr_] := { True, f }

propagateOperator[ trans_, op_ ] :=
	Transform[ op[ TheFunction[trans] ],
		   GetRMinus[trans],
		   GetRPlus[trans] ] /;
	ZForm[trans]

reallyDownsampledQ[f_, n_] :=
	Block [	{downfact},
		downfact = computeDownsamplingFactor[f, n];
		(! SameQ[downfact, 0]) && (! SameQ[downfact, 1]) &&
		  Implies[ NumberQ[downfact], IntegerQ[downfact] ] ]

SetAttributes[rootROC, {Listable}]
rootROC[x_, 0, z_] := z
rootROC[x_, y_, z_] := takeRealPower[x, 1/y]

scaleZ[t_, c_] := ScaleT[ZForm, t, c]

substituteforZ[t_, z_, newz_] := SubstituteForT[ZForm, t, z, newz]

subZ[t1_, t2_] := SubT[ZForm, t1, t2]

(* takeRealPower --  assume that both arguments are real-valued *)
SetAttributes[ takeRealPower, { Listable } ]
takeRealPower[ 0, r_ ] := 0
takeRealPower[ Infinity, r_ ] := Infinity
takeRealPower[ x_^b_, r_ ] := x^(b r) /; IntegerQ[b r]
takeRealPower[ x_, r_ ] := x^r

upsampleZ[ztrans_, z_Symbol, m_] :=
	Block [	{fz, rm, rp},
		rm = rootROC[ GetRMinus[ztrans], m, 1 ];     (* adjust ROC *)
		rp = rootROC[ GetRPlus[ztrans], m, 1 ];
		fz = TheFunction[ztrans] /. z -> z^m;  (* adjust transform *)
		Transform[fz, rm, rp] ] /;
	ZForm[ztrans] 

Zdz[t_, z_:Global`z, k_:1] := 
	Block [	{count, expr},
		expr = TheFunction[t];
		For [ count = 0, count < k, count++,
		      expr = - z SPSimplify[D[expr, z]] ];
		Transform[ expr, GetRMinus[t], GetRPlus[t] ] ] /;
	ZForm[t] && IntegerQ[k] && ( k >= 0 )


(*  R E G I O N     O F     C O N V E R G E N C E  *)

multidROC[zexpr_, oldz_] :=
	MakeZObject[ {	zexpr[[1]],
			Append[ ToList[ GetRMinus[oldz]  ], GetRMinus[zexpr] ],
			Append[ ToList[ GetRPlus[oldz]   ], GetRPlus[zexpr] ] },
		     Append[ ToList[ZVariables[oldz]], ZVariables[zexpr] ] ] /;
	ZTransformQ[zexpr] && ZTransformQ[oldz]


(*  S T A T E     D E F I N T I O N  *)

	(* in the order of their appearance in the rule base  *)

factorfield = 1			(* apply Factor[] to denominator	*)
expandfield = 2			(* apply Expand[] to expression		*)
expandallfield = 3		(* apply ExpandAll[] to expression	*)
stepfield = 4			(* multiply by (Step[n] + Step[-n - 1])	*)
thefunfield = 5			(* apply TheFunction to expression	*)
definitionfield = 6		(* apply the z-transform definition	*)

statevariables = 6

initZstate[] := Table[True, {statevariables}]


(*  B E G I N     R U L E     B A S E  *)

(*  MyZByPass--- called by forward DTFT and interfaces to MyZTransform  *)
MyZByPass[ f_, n_, z_, nlist_, zlist_, op_ ] :=
	Block [ {trans},
		completeOptions = op;
		dialogueAllFlag = SameQ[Replace[Dialogue, op], All];
		indentationString = "  ";
		simplifyFlag = TrueQ[ Replace[Simplify, op] ];
		trans = MyZTransform[f, n, z, initZstate[], nlist, zlist, op];
		MakeZObject[ trans, z ] ]

(*  Interface to MyZTransform from ztransformdriver  *)
MyZTransform[ f_, n_, z_, nlist_, zlist_, op_ ] :=
	MyZTransform[ f, n, z, initZstate[], nlist, zlist, op ]

(*   Driver for one-dimensional rule base			*)
(*   Loop until the expression to be transformed doesn't change *)
MyZTransform[ f_, n_, z_, s_, nlist_, zlist_, op_ ] :=
	Block [ {discretef, laste, newe, newop, newzrules},

		(* make f discrete *)
		discretef = ToDiscrete[ f /. CStep[a_. n] :> Step[n] ];

		(* generate the z-transform rules *)
		newzrules = TransformFixUp[ n, z, op, forwardz, True,
					    ZTransform, 0, Infinity ];
		ZTransformRules = Join[ newzrules,
					OriginalZTransformRules ];

		(* take the 1-D transform-- dialogueAllFlag is global  *)
		newop = fixUp[op];
		newe = forwardz[discretef, n, z, s, nlist, zlist, newop];
		While [ ! SameQ[ laste, newe ],
			If [ dialogueAllFlag,
			     Print[ indentationString, newe ];
			     Print[ indentationString, "which becomes" ] ];
			laste = newe;
			newe = laste /. ( forwardz[a__] :>
				 Replace[forwardz[a], ZTransformRules ] ) ];
		If [ dialogueAllFlag, Print[ indentationString, newe ] ];

		newe ]


(* Format intermediate forms so that output from Dialogue -> All is readable *)

Format[ antiCausalZ[t_, z_] ] :=
	SequenceForm[ {t}, Subscript[z -> z^-1], Subscript[" and flip ROC "] ]

Format[ downsampleZ[t_, z_, m_, ___] ] :=
	SequenceForm[ {t},
		      Subscript[" resample (ROC expands) "] ]

Format[ dz[t_, z_:Global`z, k_:1] ] := 
	SequenceForm[ ColumnForm[ {"D" Superscript[k],
				   "  " ~StringJoin~ ToString[z]} ],
		      { t } ]

Format[ forwardz[ x_, n_, z_, s_, nlist_, zlist_, op_ ] ] := 
	SequenceForm[ ColumnForm[{"Z",
				  "  " ~StringJoin~ ToString[n]}],
		      { x } ]

Format[ multiplyByExponential[t_, aexp_, z_, c_, options_] ] :=
	c SequenceForm[ {t},
			Subscript[z -> z / aexp],
			Subscript[" and scale ROC "] ]

Format[ propagateOperator[ztrans_, op_] ] := op [ ztrans ]

Format[ upsampleZ[t_, z_, m_, ___] ] :=
	SequenceForm[ {t},
		      Subscript[" resample (ROC shrinks) "] ]

Format[ Zdz[t_, z_:Global`z, k_:1] ] := 
	SequenceForm[ ColumnForm[ {"Zdz" Superscript[k],
				   "    " ~StringJoin~ ToString[z]} ],
		      { t } ]


(*  B E G I N     R U L E S  *)


ZTransformRules = {}

OriginalZTransformRules = {


(*  I.    M U L T I D I M E N S I O N A L     H O O K S			*)


(*	  A.  Region of convergence specification for dimension given	*)
(*	      by the variable n.  Allows multi-dimensional z-transforms	*)
(*	      like a^n1 Impulse[n1 - n2] Step[n1,n2] to be taken --	*)
(*	      see definition for LineImpulsemDT in "TransSupport.m"	*)
forwardz[ SignalProcessing`ROCinfo[n_, rm_:0 , rp_:Infinity], n_, z_,
		s_, nlist_, zlist_, op_ ] :>
	Transform[ 1, rm, rp ],


(*	  B.  Multidimensional line Impulses become LineImpulses.	*)
forwardz[ f_. Impulse[k_. n1_Symbol + l_. n2_Symbol], n_, z_,
		s_, nlist_, zlist_, op_ ] :>
	forwardz[ f LineImpulse[{n1,n2}, {k,-l}],
		      n, z, s, nlist, zlist, op ] /;
	SubsetQ[{n1, n2}, nlist] && MemberQ[{n1, n2}, n],


(*	  C.  Line impulses.						*)
forwardz[ f_. LineImpulse[varlist_, coefflist_], n_, z_,
		s_, nlist_, zlist_, op_ ] :>
	Block [	{functionofn, zlistmD},
		functionofn = f /. varlist ~ReplaceWith~ (n / coefflist);
		zlistmD = AssociateItem[varlist, nlist, zlist];
		lineImpulseMDZ[forwardz[functionofn, n, z, s, nlist, zlist, op], z, zlistmD, Complement[varlist,{n}]] ] /;
	FreeQ[f, LineImpulse[a__]],


(*	  D.  Multinomials						*)
forwardz[ f_. Multinomial[n1_, nvars__], n_, z_, s_, nlist_, zlist_, op_ ] :>
	Block [	{newfun},
		simplifyFlag = True;	 (* data global to this package *)
		newfun = f Binomial[Plus[n1, nvars],
				    Apply[Times, Complement[{n1, nvars},{n}]]];
		forwardz[newfun, n, z, s, nlist, zlist, op] ] /;
	SubsetQ[{n1, nvars}, nlist] && MemberQ[{n1, nvars}, n],

forwardz[ f_ (Plus[n1_, nvars__])!, n_, z_, s_, nlist_, zlist_, op_ ] :>
	forwardz[ myZmultinomialLeftOver[f, n1, nvars] Multinomial[n1, nvars],
		  n, z, s, nlist, zlist, op ] /;
	SubsetQ[{n1, nvars}, nlist] && myZmultinomialQ[f, n1, nvars],


(*	  E.  Resampling						*)
(*	      1. Upsampling						*)
forwardz[ Upsample[l_, nvars_List][f_], n_, z_, s_, nlist_, zlist_, op_ ] :>
        Block [ {dim, expr, i},
		dim = Length[nvars];
		expr = f;
		For [ i = 1, i <= dim, i++,
		      expr = Upsample[ l[[i,i]], nvars[[i]] ][expr] ];
		forwardz[ expr, n, z, s, nlist, zlist, op ] ] /;
	mDresamplingCheck[separable, l, n, nvars, nlist],

forwardz[ Upsample[l_, nvars_List][f_], n_, z_, s_, nlist_, zlist_, op_ ] :>
	upsampleSetUp[ l, nvars, f, n, z, s, nlist, zlist, op ] /;
	mDresamplingCheck[Upsample, l, n, nvars, nlist],


(*	      2. Downsampling						*)
forwardz[ Downsample[m_, nvars_List][f_], n_, z_, s_, nlist_, zlist_, op_ ] :>
        Block [ {dim, expr, i},
		dim = Length[nvars];
		expr = f;
		For [ i = 1, i <= dim, i++,
		      expr = Downsample[ m[[i,i]], nvars[[i]] ][expr] ];
		forwardz[ expr, n, z, s, nlist, zlist, op ] ] /;
	mDresamplingCheck[separable, m, n, nvars, nlist],

forwardz[ Downsample[m_, nvars_List][f_], n_, z_, s_, nlist_, zlist_, op_ ] :>
	downsampleSetUp[ m, nvars, f, n, z, s, nlist, zlist, op ] /;
	mDresamplingCheck[Downsample, m, n, nvars, nlist],




(*  II.   R A T I O N A L     Z - T R A N S F O R M S			*)


(*	  A.  Lookup rules for the zero, step, and impulse functions.	*)
forwardz[ 0, n_, z_, s_, nlist_, zlist_, op_ ] :>
	Transform[ 0, 0, Infinity ],

forwardz[ Step[n_], n_, z_, s_, nlist_, zlist_, op_ ] :>
	Transform[ 1 / ( 1 - z^-1 ), 1, Infinity ],

forwardz[ Pulse[L_, n_ + n0_.], n_, z_, s_, nlist_, zlist_, op_ ] :>
	Transform[ z^n0 ( 1 - z^(-L) ) / ( 1 - z^-1), 0, Infinity ] /;
	FreeQ[L, n] && FreeQ[n0, n],

forwardz[ a_. ( Step[n_ + b_.] - Step[n_ + c_.] ), n_, z_,
		s_, nlist_, zlist_, op_ ] :>
	allROC[subZ[forwardz[a Step[n + b], n, z, s, nlist, zlist, op],
		    forwardz[a Step[n + c], n, z, s, nlist, zlist, op]]] /;
	( b > c ),

forwardz[ a_. Step[n_ + b_.] - a_. Step[n_ + c_.] + rest_, n_, z_,
		s_, nlist_, zlist_, op_ ] :>
	addZ[allROC[subZ[forwardz[a Step[n + b], n, z, s, nlist, zlist, op],
			 forwardz[a Step[n + c], n, z, s, nlist, zlist, op]]],
	     forwardz[rest, n, z, s, nlist, zlist, op]] /;
	( b > c ),

forwardz[ a_. Impulse[n_ + n0_.], n_, z_, s_, nlist_, zlist_, op_ ] :>
	Transform[ (a /. n -> -n0 ) z^n0, 0, Infinity ],


(*	  B.  Sinusoidal functions					*)
forwardz[ Sin[b_. + w_. n_] Step[n_], n_, z_, s_, nlist_, zlist_, op_ ] :>
	Transform[ ( Sin[b] + Sin[w - b] z^-1 ) /
		   ( 1 - 2 Cos[w] z^-1 + z^-2 ), 1, Infinity ] /;
     FreeQ[{b,w}, n],					(*  [Muth, 361]  *)

forwardz[ Cos[b_. + w_. n_] Step[n_], n_, z_, s_, nlist_, zlist_, op_ ] :>
	Transform[ ( Cos[b] - Cos[w - b] z^-1 ) /
		   ( 1 - 2 Cos[w] z^-1 + z^-2 ), 1, Infinity ] /;
     FreeQ[{b,w}, n],					(*  [Muth, 361]  *)

forwardz[ Sinh[b_. + w_. n_] Step[n_], n_, z_, s_, nlist_, zlist_, op_ ] :>
	Transform[ ( Sinh[b] + Sinh[w - b] z^-1 ) /
		   ( 1 - 2 Cosh[w] z^-1 + z^-2 ), 1, Infinity ] /;
     FreeQ[{b,w}, n],					(*  [Muth, 361]  *)

forwardz[ Cosh[b_. + w_. n_] Step[n_], n_, z_, s_, nlist_, zlist_, op_ ] :>
	Transform[ ( Cosh[b] - Cosh[w - b] z^-1 ) /
		   ( 1 - 2 Cosh[w] z^-1 + z^-2 ), 1, Infinity ] /;
     FreeQ[{b,w}, n],					(*  [Muth, 361]  *)


(*	  C.  Binomial forms						*)
forwardz[ Binomial[n_, k_] Step[n_], n_, z_, s_, nlist_, zlist_, op_ ] :>
	Block [ {},
		Assuming[ k > 0, dialogueAllFlag ];
		Transform[ z^-k / (1 - z^-1)^(k + 1), 1, Infinity ] ] /;
	FreeQ[k, n] && Implies[NumberQ[k], (k > 0)],	     (* [Muth, 358] *)

forwardz[ Binomial[k_, n_] a_^n_ b_^(k_ - n_) Step[n_], n_, z_,
		s_, nlist_, zlist_, op_ ] :>
	Block [ {},
		Assuming[ k > 0, dialogueAllFlag ];
		Transform[ ( a z^-1 + b ) ^ k, -a/b, Infinity ] ] /;
	FreeQ[{a,b,k}, n] && Implies[NumberQ[k], (k > 0)],    (* [Muth, 358] *)


(*	  D.  Expand definitions of multinomial forms			*)
forwardz[ f_ Multinomial[n_, r___], n_, z_, s_, nlist_, zlist_, op_ ] :>
	Block [	{expandr, rlist},
		rlist = If [ EmptyQ[List[r]], {0}, ToList[r] ];
		expandr = Apply[ Times, Map[Factorial, rlist] ];
		forwardz [ f Plus[n, r] / ( n! expandr ), n, z,
			   s, nlist, zlist, op ] ] /;
	FreeQ[{r}, n],


(*	  E.  Hybrid binomial/multinomial forms				*)
forwardz[ (n_ + k_ + j_.)! Step[n_] / n_!, n_, z_, s_, nlist_, zlist_, op_ ] :>
	Block [ {},
		Assuming[ k > 0, dialogueAllFlag ];
		Transform[ z^j k! / ( 1 - z^-1 )^(k + 1), 1, Infinity ] ] /;
	FreeQ[{k,j}, n] && Implies[NumberQ[k], (k > 0)],	(*  ????  *)

forwardz[ Binomial[n_ + k_ + j_., k_] Step[n_], n_, z_,
		s_, nlist_, zlist_, op_ ] :>
	Block [ {},
		Assuming[ k > 0, dialogueAllFlag ];
		Transform[ z^j / ( 1 - z^-1 )^(k + 1), 1, Infinity ] ] /;
	FreeQ[{k,j}, n] && Implies[NumberQ[k], (k > 0)],	(*  ????  *)




(*  III. N O N - R A T I O N A L     Z - T R A N S F O R M S		*)


(*	  A.  Denominators are rational functions of n			*) 
forwardz[ Step[n_] / n_!, n_, z_, s_, nlist_, zlist_, op_ ] :>
	Transform[ Exp[z^-1], 0, Infinity ],		    (*  [Muth, 363]  *)	

forwardz[ Step[n_] / ( a_ n_ + b_ ), n_, z_, s_, nlist_, zlist_, op_ ] :>
	scaleZ[forwardz[Step[n] / (n + b/a), n, z, s, nlist, zlist, op], 1/a] /;
	FreeQ[{a,b}, n],	    (* using (a (n + b/a)) fails when a = -1 *)

forwardz[ Step[n_] / ( n_ + 1/2 ), n_, z_, s_, nlist_, zlist_, op_ ] :>
	Transform[ 2 Sqrt[z] ArcTan[z^-1], 0, Infinity ] /;
     FreeQ[a,n],					    (*  [Muth, 363]  *)

forwardz[ Step[n_] / ( n_ + 1 ), n_, z_, s_, nlist_, zlist_, op_ ] :>
	Transform[ -z Log[ 1 - z^-1 ], 0, Infinity ],	    (*  [Muth, 363]  *)

forwardz[ Step[n_] / ( 2 n_ )!, n_, z_, s_, nlist_, zlist_, op_ ] :>
	Transform[ Cosh[ Sqrt[z^-1] ], 0, Infinity ],	    (*  [Muth, 363]  *)

forwardz[ (2 n_)! Step[n_] / (2^n_ n_!)^2 , n_, z_, s_, nlist_, zlist_, op_ ] :>
	Transform[ Sqrt[ 1 / ( 1 - z^-1 ) ], 0, Infinity ], (*  [Muth, 364]  *)	


(*	  B.  Family of Causal Linear Phase IIR Filters			*)
forwardz[ Step[n_] / ( Gamma[1 + n_] Gamma[r_ - n_] ), n_, z_,
		s_, nlist_, zlist_, op_ ] :>
	Block [ {},
		Assuming[ Positive[r], dialogueAllFlag ];
		Transform[ (1 + z^-1)^(r - 1) / Gamma[r], 0, Infinity ] ]  /;
     FreeQ[r, z] && Implies[ NumberQ[N[r]], N[r > 0] ],	(*  [ASSP, 480-482]  *)	




(*  IV.   P R O P E R T I E S						*)


(*	  A.  Homogeneity (pick off constants)				*)
forwardz[ c_ x_., n_, z_, s_, nlist_, zlist_, op_ ] :>
	scaleZ[forwardz[x, n, z, s, nlist, zlist, op], c] /;
	FreeQ[c,n] && ! ( SameQ[c,1] && SameQ[x,1] ),	 (* [O & S, 67] *)

(*	  B.  Additivity, resulting ROC is intersection of two ROC's	*)
forwardz[ x_+y_, n_, z_, s_, nlist_, zlist_, op_ ] :>
	addZ[forwardz[x, n, z, s, nlist, zlist, op],
	     forwardz[y, n, z, s, nlist, zlist, op]],
							 (* [O & S, 67] *)

forwardz[ (x_+y_)/c_, n_, z_, s_, nlist_, zlist_, op_ ] :>
	addZ[forwardz[x/c, n, z, s, nlist, zlist, op],
	     forwardz[y/c, n, z, s, nlist, zlist, op]],
							 (* [O & S, 67] *)


(*	  C.  Shifts/delays --  every discrete function should be a	*)
(*	      function times either a Step or an Impulse, and the rule	*)
(*	      base already handles the Impulse case.			*)
forwardz[ f_. Step[n_ + m_], n_, z_, s_, nlist_, zlist_, op_ ] :>
	scaleZ[ forwardz[(f /. n -> n - m) Step[n], n, z, s, nlist, zlist, op],
		z^m ] /;
	FreeQ[m, n],

forwardz[ f_. Step[m_ - n_], n_, z_, s_, nlist_, zlist_, op_ ] :>
	scaleZ[ forwardz[(f /. n -> n + m) Step[-n], n, z, s, nlist, zlist, op],
		z^(-m) ] /;
	FreeQ[m, n],

forwardz[ x_, n_, z_, s_, nlist_, zlist_, op_ ] :>
	Block [	{n0, xnorm},
		{n0, xnorm} = GetShiftFactor[x, n];
		xnorm = xnorm /. ( n -> n - n0 );
		scaleZ[ forwardz[xnorm, n, z, s, nlist, zlist, op], z^n0 ] ] /;
	! SameQ[ First[ GetShiftFactor[x, n] ], 0 ],


(*	  D.  Anti-causal:  x[n] --> X(z)  ===>  x[-n] --> X[1/z]	*)
forwardz[ x_. Step[-n_], n_, z_, s_, nlist_, zlist_, op_ ] :>
	antiCausalZ[forwardz[x Step[-n] /. n -> -n, n, z, s, nlist, zlist, op],
		    z],


(*	  E.  Multiplication by n^m [Muth, 227]				*)
forwardz[ ZPolynomial[m_, n_] f_., n_, z_, s_, nlist_, zlist_, op_ ] :>
	scaleZ[ dz[ forwardz[ f, n, z, s, nlist, zlist, op ], z, m ],
		z^m ] /;
	FreeQ[m, n],

forwardz[ n_^m_. x_., n_, z_, s_, nlist_, zlist_, op_ ] :>
	Block [ {},
                Assuming[ m > 0, dialogueAllFlag ];
		If [ dialogueAllFlag, Print[ "where ", m, " is an integer" ] ];
		Zdz[ forwardz[x, n, z, s, nlist, zlist, op], z, m ] ] /;
	FreeQ[m,n] && Implies[NumberQ[m], IntegerQ[m] && ( m > 0 )],


(*	  F.  Integration [Muth, 231]					*)
forwardz[ x_ Step[n_ - 1] / n_, n_, z_, s_, nlist_, zlist_, op_ ] :>
	integrateZ[scaleZ[subZ[ forwardz[x Step[n], n, z, s, nlist, zlist, op],
				Limit[x, n -> 0]],
			  1/z^2],
		   z] /;
	! InfinityQ[ Limit[x / n, n -> 0] ],


(*	  G.  Multiplication by c raised to an affine function of n,	*)
(*	      which covers exponential case.				*)
forwardz[ c_^(d_. (b_. + a_. n_)) x_, n_, z_, s_, nlist_, zlist_, op_ ] :>
	multiplyByExponential [	forwardz[x, n, z, s, nlist, zlist, op],
				c^(a d), z, c^(b d), op ] /;
	FreeQ[{a,b,c,d}, n],				 (* [O & S, 67] *)


(*	  H.  Multiplication by a cosine function [Muth, 219]		*)
forwardz[ Cos[b_ + w_. n_] f_, n_, z_, s_, nlist_, zlist_, op_ ] :>
	forwardz [ Cos[b] Cos[w n] f - Sin[b] Sin[w n] f,
		   n, z, s, nlist, zlist, op ],
forwardz[ Cos[a_. n_] f_., n_, z_, s_, nlist_, zlist_, op_ ] :>
	Block [	{trans},
		trans = forwardz[f, n, z, s, nlist, zlist, op];
		scaleZ [ addZ [ substituteforZ[trans, z, Exp[I a] z],
				substituteforZ[trans, z, Exp[-I a] z] ],
			 1/2] ] /;
	FreeQ[a,n],


(*	  I.  Multiplication by a sine function [Muth, 219]		*)
forwardz[ Sin[b_ + w_. n_] f_, n_, z_, s_, nlist_, zlist_, op_ ] :>
	forwardz [ Sin[b] Cos[w n] f + Cos[b] Sin[w n] f,
		   n, z, s, nlist, zlist, op ],
forwardz[ Sin[a_. n_] f_., n_, z_, s_, nlist_, zlist_, op_ ] :>
	Block [	{trans},
		trans = forwardz[f, n, z, s, nlist, zlist, op];
		scaleZ [ subZ [ substituteforZ[trans, z, Exp[I a] z],
				substituteforZ[trans, z, Exp[-I a] z] ],
			 I/2] ] /;
	FreeQ[a,n],


(*	  J.  Conjugation:  Z{ Conj[x[n]] } --> Conj[ X ( Conj[z] ) ]	*)
forwardz[ Conjugate[x_], n_, z_, s_, nlist_, zlist_, op_ ] :>
	conjZ[forwardz[x, n, z, s, nlist, zlist, op], z],
							 (* [O&S, 67]  *)




(*  V.    D S P     S T R U C T U R E S					*)


(*	  -.  An operator independent of n---  take z-transform of f	*)
forwardz[ operator_[params__][f_], n_, z_, s_, nlist_, zlist_, op_ ] :>
	propagateOperator[ forwardz[f, n, z, s, nlist, zlist, op],
			   operator[params] ] /;
	FreeQ[{params}, n],


(*	  A.  Convolution						*)
forwardz[ Convolve[n_][x1_, x2_, rest__], n_, z_, s_, nlist_, zlist_, op_ ] :>
	multZ [ forwardz[x1, n, z, s, nlist, zlist, op],
		forwardz[Convolve[n][x2, rest], n, z, s, nlist, zlist, op] ],

forwardz[ Convolve[n_][x1_, x2_], n_, z_, s_, nlist_, zlist_, op_ ] :>
	multZ [ forwardz[x1, n, z, s, nlist, zlist, op],
		forwardz[x2, n, z, s, nlist, zlist, op] ],

forwardz[ Convolve[nconv_List][x1_, x2_], n_, z_, s_, nlist_, zlist_, op_ ] :>
	convolveZ[ Convolve[Complement[nconv, {n}]], 
		   forwardz[x1, n, z, s, nlist, zlist, op],
		   forwardz[x2, n, z, s, nlist, zlist, op] ] /;
	MemberQ[nconv, n],


(*	  B.  Difference equations [Muth, 224]				*)
forwardz[ Difference[m_, n_][f_], n_, z_, s_, nlist_, zlist_, op_ ] :>
	Block [ {},
		Assuming[ m > 0, dialogueAllFlag ];
		If [ dialogueAllFlag, Print[ "where ", m, " is an integer" ] ];
		scaleZ[ forwardz[f, n, z, s, nlist, zlist, op],
			( 1 - z^-1 ) ^ m ] ] /;
	FreeQ[m,n] && Implies[NumberQ[m], IntegerQ[m] && m > 0],

(*	  C.  Downsampling						*)

(*	      1. Downsampling operator [Crochiere & Rabiner, 34]	*)
forwardz[ Downsample[m_,n_][f_], n_, z_, s_, nlist_, zlist_, op_ ] :>
	Block [ {},
		If [ dialogueAllFlag,
		     Print[ "assuming ", m, " is an integer" ] ];
		downsampleZ[ forwardz[f, n, z, s, nlist, zlist, op], z, m ] ] /;
	FreeQ[m,n] && Implies[ NumberQ[m], IntegerQ[m] ] ,

(*	      2. Rewrite functions that are really downsampled		*)
(*               This relies on the function ScalingFactor which works  *)
(*		  best after an expression has been fully expanded.     *)
forwardz[ f_, n_, z_, s_, nlist_, zlist_, op_ ] :>
	Block [	{m},
		m = computeDownsamplingFactor[f, n];
		If [ dialogueAllFlag && ! IntegerQ[m],
		     Print[ "assuming ", m, " is an integer" ] ];
		forwardz[ Downsample[m, n][ f /. n -> (n/m) ],
			  n, z, s, nlist, zlist, op ] ] /;
	reallyDownsampledQ[f, n],


(*	  D.  Digital FIR filters.					*)
forwardz[ FIR[n_, h_, firop___], n_, z_, s_, nlist_, zlist_, op_ ] :>
	forwardz[SequenceToFunction[ToList[h], n],
		 n, z, s, nlist, zlist, op],

forwardz[ FIR[n_, h_, firop___] [ x__ ], n_, z_, s_, nlist_, zlist_, op_ ] :>
	multZ [ forwardz[FIR[n, h, firop], n, z, s, nlist, zlist, op],
		forwardz[x, n, z, s, nlist, zlist, op] ],

(*	  E.  Digital IIR filters.					*)
(*	      Region of convergence is incorrect.			*)
forwardz[ IIR[n_, a_List, iirop___], n_, z_, s_, nlist_, zlist_, op_ ] :>
	Block [	{denom, len, poles},
		len = Length[a];
		denom = a[[1]] -
			Sum[a[[index+1]] z^(- index), {index, 1, len - 1}];
		poles = GetRootList[denom, z];
		{ 1 / denom, Max[Abs[poles]], Infinity } ] /;
	VectorQ[a],

forwardz[ IIR[n_, a_, iirop___] [ x__ ], n_, z_, s_, nlist_, zlist_, op_ ] :>
	multZ [ forwardz[IIR[n, a, iirop], n, z, s, nlist, zlist, op],
		forwardz[x, n, z, s, nlist, zlist, op] ],

forwardz[ IIRFunction[n_, a_, x_, h_], n_, z_, s_, nlist_, zlist_, op_ ] :>
	multZ [ forwardz[IIR[n, a], n, z, s, nlist, zlist, op],
		forwardz[x, n, z, s, nlist, zlist, op] ],

(*	  F.  Imaginary part of a sequence [O&S, 67]			*)
forwardz[ Im[x_], n_, z_, s_, nlist_, zlist_, op_ ] :>
	Block [	{trans},
		trans = forwardz[x, n, z, s, nlist, zlist, op];
		scaleZ [ addZ [ trans,
				substituteforZ[trans, z, Conjugate[z]] ],
			 1/2] ],

(*	  G.  Periodic sequence with period k samples [Muth, 232]	*)
forwardz[ Periodic[k_,n_][f_], n_, z_, s_, nlist_, zlist_, op_ ] :>
	Block [	{fk, ntemp},
		Assuming[ k > 0, dialogueAllFlag ];
		If [ dialogueAllFlag && ! IntegerQ[k],
		     Print[ "where ", k, " is an integer" ] ];

		fk = If [ NumberQ[k],
			  SequenceToFunction[ Table[f /. n -> ntemp,
						    {ntemp, 0, k-1}],
					      n ],
			  f Pulse[k, n] ];

		scaleZ [ forwardz[fk, n, z, s, nlist, zlist, op],
			 1 / ( 1 - z^(-Abs[k]) ) ] ] /;
	FreeQ[k,n] && Implies[NumberQ[k], IntegerQ[k] && k > 0],

(*	  H.  Real part of a sequence [O&S, 67]				*)
forwardz[ Re[x_], n_, z_, s_, nlist_, zlist_, op_ ] :>
	Block [	{trans},
		trans = forwardz[x, n, z, s, nlist, zlist, op];
		scaleZ [ addZ [ trans,
				substituteforZ[trans, z, Conjugate[z]] ],
			 -I/2] ],

(*	  I.  Reverse operator	?ROC?					*)
forwardz[ Rev[n_][x_], n_, z_, s_, nlist_, zlist_, op_ ] :>
	antiCausalZ[forwardz[x, n, z, s, nlist, zlist, op], z],


(*	  J.  Shift operator						*)
forwardz[ Shift[m_,n_][x_], n_, z_, s_, nlist_, zlist_, op_ ] :>
	Block [ {},
		If [ dialogueAllFlag && ! IntegerQ[m],
		     Print[ "assuming ", m, " is an integer" ] ];
		scaleZ[forwardz[x, n, z, s, nlist, zlist, op], z^(-m)] ] /;
	FreeQ[m,n] && Implies[NumberQ[m], IntegerQ[m]],
 

(*	  K.  Signum function						*)
forwardz[ f_. Sign[g_], n_, z_, s_, nlist_, zlist_, op_ ] :>
	forwardz[ f Step[g] - f Step[-g], n, z, s, nlist, zlist, op ] /;
	! FreeQ[f, n],


(*	  L.  Summation							*)
forwardz[ Summation[i_,0,n_,1][f_], n_, z_, s_, nlist_, zlist_, op_ ] :>
	scaleZ[ forwardz[f, n, z, s, nlist, zlist, op], 1 / ( 1 - z^-1 ) ] /;
	FreeQ[i, n],				(* [Muth, 255] *)

forwardz[ Summation[i_,il_,iu_,inc_][f_], n_, z_, s_, nlist_, zlist_, op_ ] :>
	forwardz[ Summation[i, 0, Expand[iu - il], inc][f /. i -> i + il],
		  n, z, s, nlist, zlist, op ] /;
	FreeQ[{i, inc}, n] && (! FreeQ[il, n]) && (! FreeQ[iu, n]) &&
	  FreeQ[Expand[iu - il], n],

(*
forwardz[ Summation[i_,il_,iu_,inc_][f_], n_, z_, s_, nlist_, zlist_, op_ ] :>
	propagateOperator[ forwardz[f, n, z, s, nlist, zlist, op],
			   Summation[i, il, iu, inc] ] /;
	FreeQ[{i, il, iu, inc}, n],
 *)


(*	  M.  Upsampling [Crochiere & Rabiner, 36]			*)
forwardz[ Upsample[m_,n_][f_], n_, z_, s_, nlist_, zlist_, op_ ] :>
	Block [ {},
		If [ dialogueAllFlag && ! IntegerQ[m],
		     Print[ "assuming ", m, " is an integer" ] ];
		upsampleZ[ forwardz[f, n, z, s, nlist, zlist, op], z, m ] ] /;
	FreeQ[m,n] && Implies[ NumberQ[m], IntegerQ[m] && ( m > 0 ) ],




(*  V.     S T R A T E G I E S						*)


(*	   A.  Handle affine step functions.				*)
forwardz[ f_. Step[k_ n_], n_, z_, s_, nlist_, zlist_, op_ ] :>
	Block [ {},
		If [ dialogueAllFlag && ! IntegerQ[k],
		     Print[ "assuming ", k, " is an integer" ] ];
		forwardz[ f Step[n], n, z, s, nlist, zlist, op ] ] /;
	Implies[ NumberQ[k], IntegerQ[k] ],


(*	   A.  Remove any time-dependent Abs functions			*)
forwardz[ f_, n_, z_, s_, nlist_, zlist_, op_ ] :>
	forwardz[ absDialogue[f, n, op], n, z,
		  SetStateField[s, stepfield, False],
		  nlist, zlist, op ] /;
	GetStateField[s, stepfield] && ! FreeQ[ f, Abs[n] ],


(*	   B.  Collect exponential terms 				*)
forwardz[ f_. a_^(t_. n_ + k_.) / b_^(u_. n_ + l_.), n_, z_,
		s_, nlist_, zlist_, op_ ] :>
	forwardz[ (a^t/b^u)^n f a^k/b^l, n, z, s, nlist, zlist, op ],


(*	   C.  Handle functions which "blow up" at origin		*)
forwardz[ x_ Step[n_] / n_, n_, z_, s_, nlist_, zlist_, op_ ] :>
	forwardz[ Limit[x / n, n -> 0] Impulse[n] + x Step[n-1] / n, n, z,
		  s, nlist, zlist, op ] /;
	( Limit[x, n -> 0] == 0 ),


(*	   D.  Expand out product terms like (n + 1) (n + 2) ...	*)
forwardz[ x_Times, n_, z_, s_, nlist_, zlist_, op_ ] :>
	forwardz [ Distribute[x], n, z, s, nlist, zlist, op ] /;
	! SameQ[x, Distribute[x]],


(*	   E.  Factor denominator					*)
forwardz[ x_, n_, z_, s_, nlist_, zlist_, op_ ] :>
	forwardz [ Numerator[x] / Factor[Denominator[x]], n, z,
		   SetStateField[s, factorfield, False],
		   nlist, zlist, op ] /;
	GetStateField[s, factorfield] && RationalFunctionQ[x, n],


(*	   F.  Expand out numerator terms				*)
forwardz[ x_, n_, z_, s_, nlist_, zlist_, op_ ] :>
	forwardz [ Expand[x], n, z,
		   SetStateField[s, expandfield, False],
		   nlist, zlist, op ] /;
	GetStateField[s, expandfield] && ! SameQ[x, Expand[x]],


(*	   G.  Expand out numerator and denominator terms		*)
forwardz[ x_, n_, z_, s_, nlist_, zlist_, op_ ] :>
	forwardz [ ExpandAll[x], n, z,
		   SetStateField[s, expandallfield, False],
		   nlist, zlist, op ] /;
	GetStateField[s, expandallfield] && ! SameQ[x, ExpandAll[x]],


(*         H.  Use the definition					*)
forwardz[ x_ Step[n_], n_, z_, s_, nlist_, zlist_, op_ ] :>
	applyDefinition[x, n, z, s, nlist, zlist, op] /;
	GetStateField[s, definitionfield] && Replace[Definition, op],


(*	   I.  Two-sided transform					*)
forwardz[ x_, n_, z_, s_, nlist_, zlist_, op_ ] :>
	Block [	{state, trans},
		state = SetStateField[s, stepfield, False];
		trans = MyZTransform[ x Step[n] + x Step[-n - 1],
				      n, z, state, nlist, zlist, op ];
		If [ TrueQ[ZForm[trans] && TheFunction[trans] != 0],
		     If [ InformUserQ[op],
		          Message[Transform::twosided, x] ];
		     trans,
		     forwardz[ x, n, z, state, nlist, zlist, op ] ] ] /;
	GetStateField[s, stepfield],


(*	   J.  Put all signal processing objects into mathematical form	*)
forwardz[ x_, n_, z_, s_, nlist_, zlist_, op_ ] :>
	Block [	{newx, state},
		newx = ToDiscrete [ TheFunction[x] ];

		state = If [ SameQ[newx, x], s, initZstate[] ];
		state = SetStateField[state, thefunfield, False];
		state = SetStateField[state, stepfield, False];

		forwardz [ newx, n, z, state, nlist, zlist, op ] ] /;
	GetStateField[s, thefunfield]

}


(*  E N D     R U L E S  *)



(*  E N D     P A C K A G E  *)


End[]
EndPackage[]

If [ TrueQ[ $VersionNumber >= 2.0 ],
     On[ General::spell ];
     On[ General::spell1 ] ];


(*  H E L P     I N F O R M A T I O N  *)

Combine[ SPfunctions, { ZTransform } ]
Protect[ ZTransform ]


(*  E N D I N G     M E S S A G E  *)

Print["The forward z-transform rules have been loaded."]
Null
